////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2010. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.utils.swf
{

import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
import flash.utils.Endian;

import ru.goodcore.utils.StringUtils;

/**
 *  @author				Dimarik
 *  @version			1.1
 *  @langversion		3.0
 *  @playerversion		9.0
 */
public class SWFMovieClip extends ByteArray {

	//-------------------------------------------------------------------------
	//
	//   Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 * Constructor
	 * 
	 * @param input		array of source bitmapdatas
	 * @param qname		full qualified name
	 * 
	 */

	public function SWFMovieClip(input:Array/* of BitmapData */, qname:QName,
									trimTransparent:Boolean = true)
	{
		super();
		super.endian = Endian.LITTLE_ENDIAN;

		var data:Array = [
			0x09535746, 0x00000000, 0xA0000A30, 0x00010C00, 
			0x00081144, 0x02430000, 0xBFFFFFFF, 0x00000B15,
			0x53000100, 0x656E6563, 0x00003120,

			0x10000000, 0x00002E00, 0x616C660D,
			0x642E6873, 0x6C707369, 0x4D097961, 0x6569766F,
			0x70696C43, 0x624F0600, 0x7463656A, 0x616C660C,
			0x652E6873, 0x746E6576, 0x76450F73, 0x44746E65,
			0x61707369, 0x65686374, 0x69440d72, 0x616C7073,
			0x6A624F79, 0x11746365, 0x65746E49, 0x74636172,
			0x4F657669, 0x63656A62, 0x69441674, 0x616C7073,
			0x6A624F79, 0x43746365, 0x61746E6F, 0x72656E69,
			0x72705306, 0x06657469, 0x03160116, 0x06160518,
			0x09000816, 0x07020107, 0x04070402, 0x09050707,
			0x070A0207, 0x02070B02, 0x0D02070C, 0x00000003,
			0x00000000, 0x00000000, 0x01010000, 0x00030802,
			0x00000001, 0x01010201, 0x03000104, 0x09010100,
			0x30D0030A, 0x01000047, 0x0B0A0101, 0xD030D006,
			0x00470049, 0x01020200, 0xD0270901, 0x60006530,
			0x04603003, 0x30056030, 0x60300660, 0x08603007,
			0x30026030, 0x00580260, 0x1D1D1D1D, 0x681D1D1D,
			0x00004701,
			
			0x01060686, 0x00000200
		];

		var i:			uint = 0;
		var byte:		uint;
		var positionStart:	uint;

		for (; i < 11; super.writeUnsignedInt(data[i++])) {}

		positionStart = super.position;

		super.writeShort(0x0001);		
		for (; i < 13; super.writeUnsignedInt(data[i++])) void;
		super.writeShort(0x0000);

		if (qname.uri) {
			super.writeByte(0x0E);
			super.writeByte(qname.uri.length);
			super.writeUTFBytes(qname.uri);
		} else {
			super.writeShort(0x0C00);
		}
		super.writeByte(qname.localName.length);
		super.writeUTFBytes(qname.localName);

		for (; i < 19; super.writeUnsignedInt(data[i++])) {}

		byte = qname.localName.length + 1;

		if (qname.uri)
			byte += qname.uri.length;

		super.writeByte(byte);

		if (qname.uri)
			super.writeUTFBytes(qname.uri + ':');

		super.writeUTFBytes(qname.localName);

		for (; i < 79; super.writeUnsignedInt(data[i++])) void;

		this.writeTag(0x52, this, positionStart);

		var imgCount:	uint = input.length;
		var charID:		uint = 1;
		var bmpd:		BitmapData;
		var j:			uint = 0;
		var point:		Point = new Point();

		for (; j < imgCount; j++, charID += 2) {
			bmpd = BitmapData(input[j]);

			if (trimTransparent) {
				var nonzeroAlphaBounds:Rectangle = bmpd.getColorBoundsRect(0xFF000000, 0x00000000, false);
				if (nonzeroAlphaBounds.width && nonzeroAlphaBounds.height) {
					var tmpbmpd:BitmapData = new BitmapData(nonzeroAlphaBounds.width, nonzeroAlphaBounds.height, true, 0xFFFF00FF);
					tmpbmpd.copyPixels(bmpd, nonzeroAlphaBounds, point);//, null, null, true);
					this.writeDefineBitsLossless2(tmpbmpd, charID, this );
					this.writeDefineShape( nonzeroAlphaBounds, charID + 1, charID, this );
				}
			} else {
				this.writeDefineBitsLossless2( bmpd, charID, this );
				//trace(j, bmpd.getPixel32(0, 0).toString(16));
				var aaa:uint = charID;
				if (charID == 189) {
					trace(charID);
					//aaa = charID + 2;
				}
				this.writeDefineShape( bmpd.rect, charID + 1, aaa, this );
				if (charID == 189) {
					trace(charID);
				}
			}
		}


		// DefineSprite
		positionStart = super.position;
		super.writeShort(imgCount * 2 + 1);
		super.writeShort(imgCount);

		for (; i < 81; super.writeUnsignedInt(data[i++])) void;

		super.writeShort(0x0040);

		charID = 4;
		while (charID <= imgCount * 2) {
			super.writeUnsignedInt(0x01030685);
			super.writeByte(0x00);
			super.writeShort(charID);
			super.writeShort(0x0040);
			charID += 2;
		}

		super.writeShort(0x0000);
		this.writeTag(0x27, this, positionStart);


		// SymbolClass
		positionStart = super.position;
		super.writeShort(0x0001);
		super.writeShort(imgCount * 2 + 1);

		if (qname.uri)
			super.writeUTFBytes(qname.uri + '.');

		super.writeUTFBytes(qname.localName);
		super.writeByte(0x00);

		this.writeTag(0x4C, this, positionStart);

		super.writeUnsignedInt(0x000040);
		super.position = 4;
		super.writeUnsignedInt(super.length);
		super.position = 0;

		//getDefinitionNames(this);
	}

	//-------------------------------------------------------------------------
	//
	//   Private methods
	//
	//-------------------------------------------------------------------------

	/**
	 * 
	 * @param ba
	 * @param bmpd
	 * 
	 */
	private function writeDefineBitsLossless2(bmpd:BitmapData, charID:uint, ba:ByteArray):void {
		var pos:uint = ba.position;
		ba.writeShort(charID);
		ba.writeByte(0x05);
		ba.writeShort(bmpd.width);
		ba.writeShort(bmpd.height);
		var data:ByteArray = bmpd.getPixels(bmpd.rect);
		data.compress();
		ba.writeBytes( data );
		this.writeTag(0x24, ba, pos);
	}

	/**
	 * 
	 * @param ba
	 * @param bmpd
	 * 
	 */
	private function writeDefineShape(rect:Rectangle, charID:uint, BitmapId:uint, ba:ByteArray):void {
		var pos:	uint = ba.position;

		// ShapeId UI16
		ba.writeShort(charID);

		// ShapeBounds RECT
		this.writeRect(rect, ba);

		// SHAPEWITHSTYLE

		//  - FILLSTYLEARRAY
		//		- FillStyleCount UI8
		ba.writeByte(0x01);

		//		- FillStyles
		//			- FillStyleType UI8
		ba.writeByte(0x43); // Fill with 0x43 = non-smoothed clipped bitmap
		//			- BitmapId UI16
		ba.writeShort(BitmapId);
		//			- BitmapMatrix MATRIX
		
		var bitsValue:		String;
		var buffer:			String = '';
		var numBitsCount:	uint;
		var numBits:		String;

		var TranslateX:		uint = rect.x * 20;
		var TranslateY:		uint = rect.y * 20;
		var maxValue:		uint = Math.max(TranslateY, TranslateX);


		if (maxValue) {
			numBitsCount = maxValue.toString(2).length + 1;
			numBits = StringUtils.setNumberLen(numBitsCount, 5, 2);

			this.writeBytesFromString('D9 40 00 05 00 00', ba);

			buffer = '000' + numBits;

			bitsValue = StringUtils.setNumberLen(TranslateX, numBitsCount, 2);
			buffer += bitsValue;

			bitsValue = StringUtils.setNumberLen(TranslateY, numBitsCount, 2);
			buffer += bitsValue;

			while (buffer.length % 8) {
				buffer += '0';
			}

			while (buffer.length) {
				byte = parseInt( buffer.substr(0, 8), 2);
				ba.writeByte(byte);
				buffer = buffer.substr(8);
			}

		}

		else {

			this.writeBytesFromString('D9 40 00 05 00 00 00', ba);

		}

		//  - LINESTYLEARRAY
		//		- LineStyleCount UI8
		ba.writeByte(0x00);

		// NumFillBits & NumLineBits
		ba.writeByte(0x10);

		var record:			String;
		var byte:			uint;

		if (TranslateX || TranslateY) {
			// ShapeRecords
			//		- STYLECHANGERECORD [MoveBits]
			buffer += '000001';
			maxValue = Math.max(TranslateY, TranslateX);
			
			numBitsCount = maxValue.toString(2).length + 1;
			numBits = StringUtils.setNumberLen(numBitsCount, 5, 2);
			
			buffer += numBits;
			
			bitsValue = StringUtils.setNumberLen(TranslateX, numBitsCount, 2);
			buffer += bitsValue;

			bitsValue = StringUtils.setNumberLen(TranslateY, numBitsCount, 2);
			buffer += bitsValue;

		}

		var width:			uint = rect.width * 20;
		var height:			uint = rect.height * 20;

		maxValue = Math.max(width, height);

		numBitsCount = maxValue.toString(2).length + 1;
		numBits	= StringUtils.setNumberLen(numBitsCount - 2, 4, 2);

		// ShapeRecords
		//		- STYLECHANGERECORD
		buffer += '0001001';

		//		- STRAIGHTEDGERECORD
		bitsValue = StringUtils.setNumberLen(width , numBitsCount, 2);
		record = '11' + numBits + '00' + bitsValue;
		buffer += record;

		//		- STRAIGHTEDGERECORD
		bitsValue = StringUtils.setNumberLen(height, numBitsCount, 2);
		record = '11' + numBits + '01' + bitsValue;
		buffer += record;

		//		- STRAIGHTEDGERECORD
		var bitString:String = StringUtils.setNumberLen(width, numBitsCount, 2);
		var aaa:String = bitString.replace(/0/g, '2');
		aaa = aaa.replace(/1/g, '0');
		aaa = aaa.replace(/2/g, '1');
		var ddd:uint = parseInt(aaa, 2) + 1;
		bitsValue = StringUtils.setNumberLen(ddd, numBitsCount, 2, '1');
		record = '11' + numBits + '00' + bitsValue;
		buffer += record;

		//		- STRAIGHTEDGERECORD
		bitString = StringUtils.setNumberLen(height, numBitsCount, 2);
		aaa = bitString.replace(/0/g, '2');
		aaa = aaa.replace(/1/g, '0');
		aaa = aaa.replace(/2/g, '1');
		ddd = parseInt(aaa, 2) + 1;
		bitsValue = StringUtils.setNumberLen(ddd, numBitsCount, 2, '1');
		record = '11' + numBits + '01' + bitsValue;
		buffer += record;

		while (buffer.length % 8) {
			buffer += '0';
		}

		while (buffer.length) {
			byte = parseInt( buffer.substr(0, 8), 2);
			ba.writeByte(byte);
			buffer = buffer.substr(8);
		}

		

		//byte = parseInt( buffer.substr(0, 8), 2);
		//ba.writeByte(byte);
		ba.writeByte(0x00);

		// Tag type = 2
		this.writeTag(0x02, ba, pos);
	}



	private function writeRect(rectangle:Rectangle, ba:ByteArray):void {
		var xMin:Number = rectangle.x * 20;
		var xMax:Number = xMin + rectangle.width * 20;
		var yMin:Number = rectangle.y * 20;
		var yMax:Number = yMin + rectangle.height * 20;

		var max:Number = Math.max(xMax, yMax);
		var numBits:uint = max.toString(2).length + 1;

		var target:String = StringUtils.setNumberLen(numBits, 5, 2);
		target += StringUtils.setNumberLen(xMin, numBits, 2);
		target += StringUtils.setNumberLen(xMax, numBits, 2);
		target += StringUtils.setNumberLen(yMin, numBits, 2);
		target += StringUtils.setNumberLen(yMax, numBits, 2);

		var pad:uint = 0;

		if (target.length % 8) {
			pad = 8 - target.length % 8;
		}
		while (pad--) target += '0';

		var byte:uint;
		while (target.length) {
			byte = parseInt(target.substr(0, 8), 2);
			target = target.substr(8);
			ba.writeByte(byte);
		}
	}

	/**
	 * @private
	 * @param id	tag id
	 * @param ba	bytearray
	 */	
	private function writeTag(id:uint, ba:ByteArray, position:uint = 0):void {
		var length:uint = ba.length - position;

		if (length > 0x3E) {
			ba.position = position + 6;
			ba.writeBytes(ba, position);
			ba.position = position;
			ba.writeShort(id << 6 | 0x3F);
			ba.writeUnsignedInt(length);
		} else {
			ba.position = position + 2;
			ba.writeBytes(ba, position);
			ba.position = position;
			ba.writeShort(id << 6 | length);
		}

		ba.position = ba.length;
	}

	/**
	 * @private
	 * @param bytesHexString
	 * @param ba
	 */
	private function writeBytesFromString(bytesHexString:String, ba:ByteArray=null):void {
		bytesHexString = bytesHexString.replace(/\s/g, '');

		var length:uint = bytesHexString.length;

		if (!ba)
			ba = this;

		for (var i:uint = 0; i < length; i += 2) {
			var hexByte:String = bytesHexString.substr(i, 2);
			var byte:uint = parseInt(hexByte, 16);
			ba.writeByte(byte);
		}
	}

}
}

